/*------------------------------------------------------------------------------
$module:      modules
$package:     CseApi
$purpose:     CSE module managment
$author:      WAYNE WORK STDIO
$CreateAt:    08/07/04
$LastModify:
$license:     Mozilla Public License Version 1.1
------------------------------------------------------------------------------*/

#include "CseApi_.h"

#define CSE_KERNEL_REG_ID          _T("Software\\cse soft\\CseKernel20")

// declare local symbol
//==============================================================================
CseObj constSpace,constSpaceName,constNameSpace,constBase,constVarList;
CseObj constSubs,constOnExit;

STATIC CseObj CseDllHandles = NULL;
STATIC CseObj CseSymTable   = NULL;
STATIC CseObj CsePaths      = NULL;
STATIC CseObj CseArguments  = NULL;
STATIC CseObj CseOnExit     = NULL;

TCseRegister *CseRegister   = NULL;
STATIC CseObj CseModules    = NULL;

CseObj CppVoidFunc          = NULL;
CseObj CppStrType           = NULL;

// Following define TCseRegister
//==============================================================================
TBool TCseRegister::exist(const TCharArray& key)
{
    return CseRegist_exist(key);
}

TCseObj TCseRegister::get(const TCharArray& key)
{
    CseObj v = CseRegist_get(key);
    if (NULL == v)
        return dummy;
    else return v;
}

const TCseObj& TCseRegister::set(const TCharArray& key, const TCseObj& value)
{
    CseRegist_set(key,value);
    return value;
}

void TCseRegister::remove(const TCharArray& key)
{
    CseRegist_delete(key);
}

TCseObj TCseRegister::hold(const TCseObj& value)
{
    CseRegist_hold(value);
    return value;
}

// Following define TCseModules
//==============================================================================
CseObj Cse_GetModule(const TCharArray& sModule, TBool OnlyTopLevel)
{
    if (sModule.isEmpty()) return NULL;

    TCharArray sName = ExtractFileName(sModule);

    sName.upper();
    if (sName == _T("__BLTIN__"))
        return CseBltinModule;
    else if (sName == _T("__MAIN__"))
        return CseMainModule;

    // First search top modules, CseModules: [[#Name,vModule], ... ]
    TCseObj vName(sName);
    int iCount, iLen = Cse_len(CseModules);
    for (iCount = 0; iCount < iLen; iCount++)
    {
        TCseObj v = CseModules->bValue[iCount];
        if (dtBuff == v.type() && v.len() == 2)
        {
            if (vName == v[0])
                return v->bValue[1];
        }
    }
    if (OnlyTopLevel) return NULL;

    // Next search from every package
    for (iCount = 0; iCount < iLen; iCount++)
    {
        TCseObj v = CseModules->bValue[iCount];
        if (dtBuff == v.type() && v.len() == 2)
        {
            v = v[1];  // Get one top module
            CHECK_TRUE(v.isSpace());
            
            TCseObj bSubs = v.get(constSubs); // [sModule,#name, ...]
            if (bSubs == dummy) continue;
            ASSERT_TRUE(bSubs.type() == dtBuff);

            for (int index = 1; index < bSubs.len(); index += 2)
            {
                TCharArray sDesc = bSubs[index-1].beStr();
                if (sName == sDesc.upper())
                {
                    TCseObj value = v.get(bSubs[index]);
                    if (value.isSpace()) return value;
                }
            }
        }
    }
    return NULL;
}

STATIC void TryRunExitFunc(const TCseObj& vFunc, TBool safeExit)
{
    if (vFunc.type() == dtInstance && vFunc.len() == 3 && vFunc[0] == CppVoidFunc)
    {
        TCseObj v = vFunc[1];
        if (v.type() == dtInt)
        {
            void (*func)() = (void (*)()) (v->iValue + vFunc[2]->iValue);

            if (safeExit)
            {   // if meet error, just ignore it
                try
                {
                    func();
                }
                catch(...)
                {}
            }
            else func();
        }
    }
}

void Cse_ClearModContext(const TCseObj& CurrMod)
{   // remove none-reserved data
    PEntryPoint pEntry = CurrMod->entry + 6;
    TEntryArray& Array = CurrMod.dArray();
    while (Array.enumItem(pEntry))
    {
        TStolenObj vOld = pEntry->value;
        pEntry->value = dummy;
    }
}

void Cse_ClearModule(const TCseObj& CurrMod, TBool safeExit)
{
    ASSERT_TRUE(CurrMod.isSpace());

    // run every item in bOnExit
    TCseObj bOnExit = CurrMod.quickGet(constOnExit);
    if (bOnExit != dummy && bOnExit.type() == dtBuff)
    {
        while (bOnExit.len() > 0)
        {
            TCseObj vItem = bOnExit.pop();
            TryRunExitFunc(vItem,safeExit);
        }
    }

    // remove none-reserved data
    Cse_ClearModContext(CurrMod);

    // remove reserved data
    TCseObj bSubs = CurrMod.quickGet(constSubs);  // #5
    if (bSubs.type() == dtBuff) bSubs.clear();
    CurrMod.set(constSubs,CseNull);

    bSubs = CurrMod.quickGet(constVarList);       // #4
    if (bSubs.type() == dtBuff) bSubs.truncate(2);
    CurrMod.set(constVarList,CseNull);

    CurrMod.set(constBase,CseNull);               // #3
    CurrMod.set(constNameSpace,CseNull);          // #2
    CurrMod.set(constSpaceName,CseNull);          // #1
    CurrMod.set(constSpace,CseNull);              // #0
    CurrMod.clear();
}

void Cse_SetModule(const TCharArray& sModule, CseObj module)
{
    // __bltin__ and __main__ is special module, not need save to module list
    if (module == CseBltinModule || module == CseMainModule) return;

    TCseObj vName(TCharArray(sModule).upper());

    if (module == dummy) // remove module
    {
        int iLen = Cse_len(CseModules);
        for (int iCount = 0; iCount < iLen; iCount++)
        {
            TCseObj v = CseModules->bValue[iCount];
            if (v.type() == dtBuff && v.len() == 2 && v[0] == vName)
            {
                TCseObj CurrMod = v[1];
                if (CurrMod.isSpace())
                {
                    CseBuff_remove(CseModules,iCount);
                    Cse_ClearModule(CurrMod,false);
                }
                break;
            }
        }
        return;
    }

    if (dtDict != Cse_type(module)) return;
    while(true)
    {
        TBool OldExists = false;
        int iLen = Cse_len(CseModules);
        for (int iCount = 0; iCount < iLen; iCount++)
        {
            TCseObj v = CseModules->bValue[iCount];
            if (dtBuff == v.type() && v.len() == 2)
            {
                // if same module exists, adjust module name
                if (module == v[1])
                {
                    if (v[0] != vName)
                        v[0] = vName;
                    return;
                }

                // if same name exists, remove old module value
                else if (v[0] == vName)
                {
                    CseBuff_remove(CseModules,iCount);
                    OldExists = true;
                    break;
                }
            }
        }
        if (! OldExists) break;
    }

    CseObj vMod = CseBuff_StolenAppend(CseModules,CseBuff_new(0));
    CseBuff_append(vMod,vName);
    CseBuff_append(vMod,module);
}

TBool Cse_SearchFile(TCharArray& ret, const TCharArray& sModule, const TCharArray& RefFile)
{
    // first, check absolute directory and DefaultDir
    if (sModule.isEmpty()) return false;

    if (sModule.find(_T(':')) != -1)
    {   // Current file is absolute, no need search in PATH
        if (FileExists(sModule))
        {
            ret = sModule;
            return true;
        }
        else return false;
    }
    else
    {
        if ( ! RefFile.isEmpty() )
        {   // if "import ModuleB" in ModuleA, try search ModuleA path
            if ( RefFile.find(_T(':') != -1) )
            {   // RefFile is absolute
                TCharArray sTmp = ExtractFileDir(RefFile) + _T('\\') + sModule;
                sTmp = NormalizeFileName(sTmp);
                if (FileExists(sTmp))
                {
                    ret = sTmp;
                    return true;
                }
            }
        }
    }

    // then, try find from lib paths
    TCharArray sAppBase = ExtractFileDir(GetCurrExeFileName()) + _T('\\');
    for (int iCount = 0; iCount < Cse_len(CsePaths); iCount++)
    {
        CseObj v = CsePaths->bValue[iCount];
        if (dtStr == Cse_type(v))
        {
            TCharArray AName;
            Cse_BeStr(AName,v);
            CHECK_TRUE(! AName.isEmpty());
            
            TXchar ch = AName[-1];
            if (ch != _T('\\') && ch != _T('/'))
                AName += _T('\\');
            if (AName.find(_T(':')) == -1)  // is relative
                AName = sAppBase + AName + sModule;
            else AName = AName + sModule;   // is absolute
            
            AName = NormalizeFileName(AName);
            if ( FileExists(AName) )
            {
                ret = AName;
                return true;
            }
        }
    }

    return false;
}

CseObj Cse_LoadCsd(const TCharArray& sModule, const TCharArray& RefFile)
{
    TCharArray sFile = sModule;
    TCharArray sExt = ExtractFileExt(sFile).upper();

    if ( sExt != _T("CSD") && sExt != _T("CSB") )
        sFile += _T(".csd");

    CseObj ret = Cse_GetModule(sFile,false);
    if (NULL != ret) return ret;

    TCharArray sLocate;
    if (Cse_SearchFile(sLocate,sFile,RefFile))
    {
        TDllFile dll(sLocate);
        if (dll.isLoaded())
        {
            CseObj (*LoadFunc)() = (CseObj (*)()) dll.getSymbol(_T("__entry__"));
            if (NULL != LoadFunc)
            {
                ret = LoadFunc();
                if (NULL == ret || dtDict != Cse_type(ret))
                    throw LOG_CSE_ERR(CSE_IO_ERR,_T("CSD format error:") + sLocate);

                void *h = dll.detach(); // detach dll handle
                CseBuff_StolenAppend(CseDllHandles,CseInt_new((TInt) h));
                return ret;
            }
        }
        throw LOG_CSE_ERR(CSE_IO_ERR,_T("load CSD file error:") + sLocate);
    }
    else throw LOG_CSE_ERR(CSE_IO_ERR,_T("read file error:") + sFile);
}

TBool Cse_UseModule(CseObj TargModule, CseObj SourModule)
{
    ASSERT_TRUE(dtDict == Cse_type(TargModule) && dtDict == Cse_type(SourModule));

    PEntryPoint pEntry = TargModule->entry + 2;
    if (pEntry->key == constNameSpace && pEntry->value != NULL)
    {
        TCseObj modules = pEntry->value;
        if (modules.type() == dtBuff)
        {
            for (int i = 0; i < modules.len(); i++)
                if (modules->bValue[i] == SourModule)
                    return true;  // source module has exists

            // add imported module
            modules.append(SourModule);
            return true;
        }
    }
    
    return false;
}

void Cse_UnloadModules(TBool safeExit)
{
    // Run every top module's onExit-process
    TCseObj modules = CseModules;
    while (modules.len() > 0)
    {
        TCseObj vMod = modules.pop();
        if (vMod.type() == dtBuff && vMod.len() == 2)
        {
            TCseObj CurrMod = vMod->bValue[1];
            if (CurrMod.type() == dtDict)
                Cse_ClearModule(CurrMod,safeExit);
        }
    }

    // Unload every DLL
    TCseObj Dlls = CseDllHandles;
    while (Dlls.len() > 0)
    {
        TCseObj vItem = Dlls.pop();
        TDllFile::unloadLib((void *) vItem->iValue);
    }
}

CseObj Cse_NewModule(const TCharArray& sModule)
{
    CseObj ret = CseDict_new(16);
    TCseObj& vMod = (TCseObj&) ret;
    
    vMod.set(constSpace,TCseObj(CSE_WEAK,ret));
    vMod.set(constSpaceName,TCseObj(CSE_ID,sModule));
    vMod.set(constNameSpace,TCseObj(CSE_BUFF));
    vMod.set(constBase,CseNull);

    // default bVarList: [dummy,dEmpty]
    vMod.set(constVarList,TCseObj(_T("[v,v]"),dummy,CseEmptySpace));

    // default bSubMod: []
    vMod.set(constSubs,TCseObj(CSE_BUFF));

    // default bOnExit: []
    vMod.set(constOnExit,TCseObj(CSE_BUFF));

    ASSERT_TRUE((ret->entry + 6)->key == constOnExit); // hash of #6 is 6
    return ret;
}

int Cse_AddSymAddr(TInt iAddr)
{
    int ret;
    CseTier_append(CseSymTable,iAddr,&ret);
    return ret;
}

TBool Cse_AddSubModule(CseObj module, const TCharArray& name, CseObj SubModule)
{
    ASSERT_TRUE(dtDict == Cse_type(module) && dtDict == Cse_type(SubModule));

    PEntryPoint pEntry = module->entry + 5;
    if (pEntry->key == constSubs && pEntry->value != NULL)
    {
        TCseObj ModList = pEntry->value;
        if (ModList.type() == dtBuff)
        {
            TBool hasAdd = false;
            for (int i = 1; i < ModList.len(); i += 2)
            {
                if (name == ModList[i-1].beStr())
                {   // same sub-module name exists
                    ModList[i] = SubModule;
                    hasAdd = true;
                    break;
                }
            }
            
            // add newly module
            TCseObj vId(CSE_ID,name);
            if (! hasAdd)
            {
                CseBuff_append(pEntry->value,vId);  // alis name
                CseBuff_append(pEntry->value,vId);  // formal name
            }
            CseDict_set(module,vId,SubModule);
            return true;
        }
    }
    return false;
}

int Cse_AddExitFunc(CseObj module, void *func)
{
    ASSERT_TRUE(dtDict == Cse_type(module));

    PEntryPoint pEntry = module->entry + 6;
    if (pEntry->key == constOnExit && pEntry->value != NULL && Cse_type(pEntry->value) == dtBuff)
    {
        int ret;
        TCseObj bOnExit = pEntry->value;
        TCseObj vCmd = bOnExit.append(TCseObj(CSE_INSTANCE),&ret);
        vCmd.append(CppVoidFunc);
        vCmd.append(TIntObj((TInt)func));
        vCmd.append(TIntObj(0));

        return ret;
    }
    return -1;
}

int Cse_AddModuleVar(CseObj module, CseObj var)
{
    ASSERT_TRUE(dtDict == Cse_type(module));

    PEntryPoint pEntry = module->entry + 4;
    if (pEntry->key == constVarList && pEntry->value != NULL)
    {   // save special variable to module.#4
        TCseObj SymList = pEntry->value;
        if (SymList.type() == dtBuff)
        {
            int index;
            SymList.append(var,&index);
            return index;
        }
    }
    return -1;
}

TCseModule::TCseModule(const TCharArray& sModule, TBool OnlyTopLevel): TCseObj()
{
    CseObj mod = Cse_GetModule(sModule,OnlyTopLevel);
    if (mod == NULL)
    {
        TCseModule mod = create(sModule,true);
        m_data = Cse_IncRefEx(mod.m_data);
    }
    else m_data = Cse_IncRefEx(mod);
}

TCseModule::TCseModule(const TCseObj& module): TCseObj(module)
{
    CHECK_MSG(isSpace(),_T("module format error"));
}

TCharArray TCseModule::searchFile(const TCharArray& sModule, const TCharArray& RefFile)
{
    TCharArray ret;
    if (Cse_SearchFile(ret,sModule,RefFile))
        return TCharArray(CUT_FROM,ret);
    else return CseEmptyStr;
}

TCseObj TCseModule::getModule(const TCharArray& sModule, TBool OnlyTopLevel)
{
    return Cse_GetModule(sModule,OnlyTopLevel);
}

void TCseModule::setModule(const TCharArray& sModule, const TCseObj& mod)
{   // if mod == dummy, means remove module
    Cse_SetModule(sModule,mod);
}

TCseModule TCseModule::create(const TCharArray& sModule, TBool regist)
{
    TStolenObj ret = Cse_NewModule(sModule);
    if (regist)
        Cse_SetModule(ExtractFileName(sModule),ret);
    return (CseObj)ret;
}

TCseModule TCseModule::loadCsd(const TCharArray& sModule, const TCharArray& RefFile)
{
    return Cse_LoadCsd(sModule,RefFile);
}

void TCseModule::unloadAll(TBool safeExit)
{
    Cse_UnloadModules(safeExit);
}

TBool TCseModule::uses(const TCseModule& module) const
{
    return Cse_UseModule(m_data,module.m_data);
}

void TCseModule::clear() const
{
    Cse_ClearModule(m_data,false);
}

const TCseObj& TCseModule::attrSet(const TCharArray& attr, const TCseObj& value) const
{
    TCseObj id(CSE_ID,attr);
    return set(id,value);
}

TCseObj TCseModule::attrGet(const TCharArray& attr) const
{
    TCseObj id(CSE_ID,attr);
    return get(id);
}

int TCseModule::addSymAddr(TInt iAddr) const
{
    int index = Cse_AddSymAddr(iAddr);
    TInt *pSymIndex = &m_data->entry[0].value->iFlag;
    if (*pSymIndex == 0) *pSymIndex = index;
    
    return index;
}

int TCseModule::addExitFunc(void *func) const
{
    return Cse_AddExitFunc(m_data,func);
}

int TCseModule::addModuleVar(const TCseObj& var) const
{
    return Cse_AddModuleVar(m_data,var);
}

TBool TCseModule::addSubModule(const TCharArray& name, const TCseObj& SubModule) const
{
    return Cse_AddSubModule(m_data,name,SubModule);
}

// Following define module init and exit
//==============================================================================
void SetupDefaultPath(void)
{   // add current EXE file path
    TCharArray AppDir = ExtractFileDir(GetCurrExeFileName());
    CseBuff_StolenAppend(CsePaths,CseStr_new(AppDir));
    CseBuff_StolenAppend(CsePaths,CseStr_new(AppDir + _T("\\testcase")));

    TCharArray sLibDir(_T('\0'),256);
    TRegConfig config(HKey_LocalMachine,CSE_KERNEL_REG_ID);

    if (config.read(_T("HomePath"),sLibDir.addr()))
    {
        sLibDir.truncate(sLibDir.len() - 1); // configure is string, included '\0' also
        CseBuff_StolenAppend(CsePaths,CseStr_new(sLibDir));
        
        TCharArray sOut;
        if (SearchSubDirs(sOut,sLibDir))
        {
            size_t i = 0, iLen = sOut.len();
            while (i < iLen)
            {
                TXchar *sItem = sOut.addr() + i;
                TCharArray s = sItem;

                if (! s.isEmpty())
                {
                    CseBuff_StolenAppend(CsePaths,CseStr_new(s));
                }
                i += s.len() + 1;
            }
        }
    }
}

CseObj Cse_BeStr_(CseObj value)
{   // void Cse_BeStr(TCharArray& ret, CseObj value);
    TCharArray ret;
    Cse_BeStr(ret,value);
    return CseStr_NewEx(ret);
}

CseObj CseStr_new_(TXchar ch, int n)
{
    TCharArray s(ch,n);
    return CseStr_NewEx(s);
}

void CseStr_AssignEx_(CseObj TargStr, TXchar *sDesc, int n)
{
    TCharArray s(sDesc,n);
    CseStr_assign(TargStr,s);
}

void CseStr_joinEx_(CseObj TargStr, TXchar *SourStr, int iLen, int index)
{   // void CseStr_join(CseObj TargStr, const TCharArray& SourStr, int index)
    TCharArray s(SourStr,iLen);
    CseStr_join(TargStr,s,index);
}

PEntryPoint CseDict_IdEntry_(CseObj dict, CseObj Id)
{
    ASSERT_TRUE(Cse_type(Id) == dtId);
    TCharArray *s = (TCharArray *) &Id->sValue;
    return CseDict_IdEntry(dict,*s,Id->hash);
}

PEntryPoint CseDict_IntEntry_(CseObj dict, CseObj iObj)
{
    ASSERT_TRUE(Cse_type(iObj) == dtInt);

    TEntryArray *EntryArray = (TEntryArray *) &dict->entry;
    return EntryArray->intEntry(iObj->iValue);
}

CseObj CseDict_EntryValue_(CseObj dict, CseObj key)
{
    TEntryArray *EntryArray = (TEntryArray *) &dict->entry;
    CseObj v = EntryArray->get(key);
    if (v == dummy)
        return NULL;
    else return v;
}

CseObj _CseId_new(TXchar *sDesc, int n)
{
    TCharArray s(sDesc,n);
    return CseId_NewEx(s);
}

TBool CseRegist_exist_(const TXchar *key)
{   // TBool CseRegist_exist(const TCharArray& key)
    return CseRegist_exist(key);
}

CseObj CseRegist_get_(const TXchar *key)
{   // CseObj CseRegist_get(const TCharArray& key)
    return CseRegist_get(key);
}

CseObj CseRegist_StolenSet_(const TXchar *key, CseObj value)
{   // CseObj CseRegist_StolenSet(const TCharArray& key, CseObj value)
    return CseRegist_StolenSet(key,value);
}

CseObj CseRegist_set_(const TXchar *key, CseObj value)
{   // CseObj CseRegist_set(const TCharArray& key, CseObj value)
    return CseRegist_set(key,value);
}

void CseRegist_delete_(const TXchar *key)
{   // void CseRegist_delete(const TCharArray& key)
    CseRegist_delete(key);
}

void CseModules_SetArg(int argc, TXchar **argv)
{
    for (int i = 0; i < argc; i++)
        CseBuff_StolenAppend(CseArguments,CseStr_new(argv[i]));
}

STATIC TBool ModuleIsInit = false;
STATIC int ModuleInitCount = 0;

void CseModules_init()
{
    ModuleInitCount++;
    if (ModuleInitCount > 1) return;  // only execute at first time
    
    if (! ModuleIsInit)
    {
        ModuleIsInit = true;

        BaseTypes_init();
        
        constSpace      = CseRegist_StolenHold(CseId_new(_T("#0"))); // __space__
        constSpaceName  = CseRegist_StolenHold(CseId_new(_T("#1"))); // __name__
        constNameSpace  = CseRegist_StolenHold(CseId_new(_T("#2"))); // __namespace__
        constBase       = CseRegist_StolenHold(CseId_new(_T("#3"))); // __base__
        constVarList    = CseRegist_StolenHold(CseId_new(_T("#4"))); // __symbol__
        constSubs       = CseRegist_StolenHold(CseId_new(_T("#5"))); // __init__
        constOnExit     = CseRegist_StolenHold(CseId_new(_T("#6"))); // __del__

        // initial system environment
        CseDllHandles   = CseRegist_StolenHold(CseBuff_new(0));
        CseSymTable     = CseRegist_StolenSet(_T("KERNEL/SYM_TABLE"),CseTier_new(2));
        CsePaths        = CseRegist_StolenSet(_T("KERNEL/PATHS"),CseBuff_new(0));
        CseArguments    = CseRegist_StolenSet(_T("KERNEL/ARGS"),CseBuff_new(0));
        CseOnExit       = CseRegist_StolenSet(_T("KERNEL/ON_EXIT"),CseBuff_new(0));
        CppVoidFunc     = CseRegist_StolenSet(_T("KERNEL/VOID_FUNC"),CseBuff_new(0));
        CppStrType      = CseRegist_StolenSet(_T("KERNEL/CPPSTR_TYPE"),CseDict_new());
                          CseRegist_StolenSet(_T("KERNEL/RUN_STATE"),CseInt_new(0));
        SetupDefaultPath();

        // create CSE register interface: CseRegister
        CseRegister     = new TCseRegister();

        // create CSE module interface: CseModules
        CseModules      = CseBuff_new(0);
        CseBltinModule  = Cse_NewModule(_T("__bltin__")); // special builtin module
        CseMainModule   = Cse_NewModule(_T("__main__"));  // special builtin module

        // regist C style API
        //-------------------------------------
        TIntArray& tArray = CseRegister->set(_T("KERNEL/KERNEL_API"),TCseObj(CSE_TIER)).tArray();
        tArray.append((TInt) CSE_API_VERSION);                // 0
        tArray.append((TInt) CSE_FEATURE_FLAG);               // 1
        tArray.append((TInt) 0);                              // reserved: 2
        tArray.append((TInt) 0);                              // reserved: 3
        tArray.append((TInt) &CseDict_IdEntry_);              // 4
        tArray.append((TInt) &CseDict_IntEntry_);             // 5

        tArray.append((TInt) dummy);                          // 6
        tArray.append((TInt) CseNull);                        // 7
        tArray.append((TInt) CseBltinModule);                 // 8
        tArray.append((TInt) CseMainModule);                  // 9
        tArray.append((TInt) CseEmptySpace);                  // 10
        tArray.append((TInt) CseThreadStacks);                // 11
        
        tArray.append((TInt) &Cse_CloseRefCheck);             // 12
        tArray.append((TInt) &Cse_free);                      // 13
        tArray.append((TInt) &Cse_GetTypeName);               // 14

        //-------------------------------------
        tArray.append((TInt) &Cse_IncRef);                    // 15
        tArray.append((TInt) &Cse_DecRef);                    // 16
        tArray.append((TInt) &Cse_IncRefEx);                  // 17
        tArray.append((TInt) &Cse_RefCount);                  // 18

        tArray.append((TInt) &Cse_type);                      // 19
        tArray.append((TInt) &Cse_len);                       // 20
        tArray.append((TInt) &Cse_FixSize);                   // 21
        tArray.append((TInt) &Cse_hash);                      // 22
        tArray.append((TInt) &Cse_ObjComp);                   // 23
        tArray.append((TInt) &Cse_compare);                   // 24
        tArray.append((TInt) &Cse_equal);                     // 25

        tArray.append((TInt) &Cse_GetFlag);                   // 26
        tArray.append((TInt) &Cse_SetFlag);                   // 27
        tArray.append((TInt) &Cse_OptionOn);                  // 28
        tArray.append((TInt) &Cse_SetOption);                 // 29
        tArray.append((TInt) &Cse_ClearOption);               // 30

        tArray.append((TInt) &Cse_unlink);                    // 31
        tArray.append((TInt) &Cse_BeStr_);                    // 32
        tArray.append((TInt) &Cse_StrEx);                     // 33
        tArray.append((TInt) 0);                              // 34, reserved

        //-------------------------------------
        tArray.append((TInt) &CseInt_new);                    // 35
        tArray.append((TInt) &CseWint_new);                   // 36
        tArray.append((TInt) &CseFloat_new);                  // 37

        CseObj (*_StrNew)(const TXchar *,int) = &CseStr_new;
        tArray.append((TInt) _StrNew);                        // 38
        tArray.append((TInt) &CseStr_new_);                   // 39
        
        tArray.append((TInt) &CseStr_append);                 // 40
        tArray.append((TInt) &CseStr_get);                    // 41
        tArray.append((TInt) &CseStr_set);                    // 42
        tArray.append((TInt) &CseStr_insert);                 // 43
        tArray.append((TInt) &CseStr_slice);                  // 44
        tArray.append((TInt) &CseStr_remove);                 // 45
        tArray.append((TInt) &CseStr_clear);                  // 46
        tArray.append((TInt) &CseStr_AssignEx_);              // 47

        void (*_StrAssign)(CseObj,CseObj) = &CseStr_assign;
        tArray.append((TInt) _StrAssign);                     // 48
        void (*_StrJoin)(CseObj,CseObj,int) = &CseStr_join;
        tArray.append((TInt) _StrJoin);                       // 49
        tArray.append((TInt) &CseStr_joinEx_);                // 50
        tArray.append((TInt) &CseStr_SortAdd);                // 51
        tArray.append((TInt) &CseStr_sort);                   // 52
        tArray.append((TInt) &CseStr_upper);                  // 53
        tArray.append((TInt) &CseStr_lower);                  // 54
        tArray.append((TInt) &CseStr_trim);                   // 55
        tArray.append((TInt) &CseStr_TrimLeft);               // 56
        tArray.append((TInt) &CseStr_TrimRight);              // 57
        tArray.append((TInt) &CseStr_truncate);               // 58
        tArray.append((TInt) &CseStr_hash);                   // 59
        
        int (*_StrFind1)(CseObj,TXchar,TBool) = &CseStr_find;
        int (*_StrFind2)(CseObj,const TXchar *,int) = &CseStr_find;
        tArray.append((TInt) _StrFind1);                      // 60
        tArray.append((TInt) _StrFind2);                      // 61
        tArray.append((TInt) &CseStr_ToInt);                  // 62
        tArray.append((TInt) &CseStr_ToWInt);                 // 63
        tArray.append((TInt) &CseStr_ToDouble);               // 64

        //-------------------------------------
        CseObj (*_TierNew)(int) = &CseTier_new;
        tArray.append((TInt) _TierNew);                       // 65
        tArray.append((TInt) &CseTier_append);                // 66
        tArray.append((TInt) &CseTier_get);                   // 67
        tArray.append((TInt) &CseTier_set);                   // 68
        tArray.append((TInt) &CseTier_insert);                // 69
        tArray.append((TInt) &CseTier_slice);                 // 70
        tArray.append((TInt) &CseTier_remove);                // 71
        tArray.append((TInt) &CseTier_clear);                 // 72
        
        void (*_TierAssign)(CseObj,CseObj) = &CseTier_assign;
        tArray.append((TInt) _TierAssign);                    // 73
        void (*_TierJoin)(CseObj,CseObj,int) = &CseTier_join;
        tArray.append((TInt) _TierJoin);                      // 74
        tArray.append((TInt) &CseTier_SortAdd);               // 75
        tArray.append((TInt) &CseTier_sort);                  // 76
        tArray.append((TInt) &CseTier_truncate);              // 77

        //-------------------------------------
        CseObj (*_BuffNew)(int) = &CseBuff_new;
        tArray.append((TInt) _BuffNew);                       // 78
        tArray.append((TInt) &CseBuff_StolenAppend);          // 79
        tArray.append((TInt) &CseBuff_append);                // 80
        tArray.append((TInt) &CseBuff_get);                   // 81
        tArray.append((TInt) &CseBuff_set);                   // 82
        tArray.append((TInt) &CseBuff_insert);                // 83
        tArray.append((TInt) &CseBuff_slice);                 // 84
        tArray.append((TInt) &CseBuff_remove);                // 85
        tArray.append((TInt) &CseBuff_clear);                 // 86

        void (*_BuffAssign)(CseObj,CseObj) = &CseBuff_assign;
        tArray.append((TInt) _BuffAssign);                    // 87
        void (*_BuffJoin)(CseObj,CseObj,int) = &CseBuff_join;
        tArray.append((TInt) _BuffJoin);                      // 88
        tArray.append((TInt) &CseBuff_SortAdd);               // 89
        tArray.append((TInt) &CseBuff_sort);                  // 90
        tArray.append((TInt) &CseBuff_truncate);              // 91
        tArray.append((TInt) &CseBuff_ToNotator);             // 92

        //-------------------------------------
        CseObj (*_DictNew)(int) = &CseDict_new;
        tArray.append((TInt) _DictNew);                       // 93
        tArray.append((TInt) &CseDict_EntryValue_);           // 94
        tArray.append((TInt) &CseDict_StolenSet);             // 95
        tArray.append((TInt) &CseDict_set);                   // 96
        tArray.append((TInt) &CseDict_SetEx);                 // 97
        tArray.append((TInt) &CseDict_clear);                 // 98

        void (*_DictAssign)(CseObj,CseObj) = &CseDict_assign;
        tArray.append((TInt) _DictAssign);                    // 99
        void (*_DictJoin)(CseObj,CseObj) = &CseDict_join;
        tArray.append((TInt) _DictJoin);                      // 100
        tArray.append((TInt) &CseDict_StolenBind);            // 101
        tArray.append((TInt) &CseDict_bind);                  // 102
        tArray.append((TInt) &CseDict_GetBinding);            // 103
        tArray.append((TInt) &CseDict_enumItem);              // 104

        //-------------------------------------
        tArray.append((TInt) &CseNotator_new);                // 105
        tArray.append((TInt) &CseStrong_new);                 // 106
        tArray.append((TInt) &CseStrong_StolenSet);           // 107
        tArray.append((TInt) &CseStrong_set);                 // 108

        tArray.append((TInt) &_CseId_new);                    // 109
        tArray.append((TInt) &CseWeak_new);                   // 110
        tArray.append((TInt) &CseInstance_new);               // 111

        //-------------------------------------
        tArray.append((TInt) &CseRegist_exist_);              // 112
        tArray.append((TInt) &CseRegist_get_);                // 113
        tArray.append((TInt) &CseRegist_StolenSet_);          // 114
        tArray.append((TInt) &CseRegist_set_);                // 115
        tArray.append((TInt) &CseRegist_delete_);             // 116
        
        tArray.append((TInt) &CseRegist_hold);                // 117
        tArray.append((TInt) &CseRegist_StolenHold);          // 118
    }
}

void CseModules_exit()
{
    ModuleInitCount--;
    if (ModuleInitCount > 0) return;  // only execute at last time
    
    if (ModuleIsInit)
    {
        ModuleIsInit = false;

        Cse_ClearModContext(CseMainModule);
        TCseModule::unloadAll(true);
        Cse_ClearModContext(CseBltinModule);

        Cse_free(CseModules,true);
        delete CseRegister;
        
        BaseTypes_exit();
    }
}

extern "C" {

CSE_IMPEXP_IN void Cse_SetArgument(int argc, TXchar **argv)
{
    CseModules_SetArg(argc,argv);
}

CSE_IMPEXP_IN void Cse_InitModules()
{
    CseModules_init();
}

CSE_IMPEXP_IN void Cse_ExitModules(void)
{
    CseModules_exit();
}

} // end of extern "C"
